package com.lwp.i18n.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lwp.i18n.util.excel.I18nExcel;
import com.lwp.i18n.util.excel.I18nTranslationData;
import com.lwp.i18n.util.excel.I18nTranslationDataListener;
import com.lwp.i18n.util.js.JsParseVO;
import com.lwp.i18n.util.js.JsParser;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 转化js文件
 */
public class JsI18nProcessor {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private String sourcePath;

    /**
     * 国际化文件使用的全局对象名
     */
    private String i18nGlobal = "I18nMsg";

    /**
     * 默认的key前缀
     * kg 为 keygen
     */
    private String i18nKeyPrefix = "kg.";

    /**文件编码*/
    private static final String ENCODING = "utf-8";

    /**
     * html注释<!-- XXXX -->
     */
    private static String regEx_html = "<!--.*?-->";
    private static Pattern p_html = Pattern.compile(regEx_html, Pattern.DOTALL);
    /**
     * js多行注释
     */
    // js注释/* XXXX */
    private static String regEx_js1 = "/\\*.*?\\*/";
    private static Pattern p_js1 = Pattern.compile(regEx_js1, Pattern.DOTALL);
    /**
     * js单行注释 // XXXXX
     */
//    private static String regEx_js2 = "//[\\s\\S]*?\n";
    private static String regEx_js2 = "//[\\s\\S]*?\n";
    private static Pattern p_js2 = Pattern.compile(regEx_js2, Pattern.CASE_INSENSITIVE);

    public List<I18nBean> getAllI18nMsgList() {
        return allI18nMsgList;
    }

    public void setAllI18nMsgList(List<I18nBean> allI18nMsgList) {
        this.allI18nMsgList = allI18nMsgList;
    }

    private Map<String, I18nBean> i18nMap = new HashMap<>();

    private List<I18nBean> allI18nMsgList = new ArrayList<>();

    /**
     * 有问题文件的路径为key, 问题内容为 val。 问题内容按文件存放，多处有问题也放在一个val中。
     */
    private Map<String, String> problemFileMap = new HashMap<>();

    private String i18nTranslationFileName = "i18n_translation.xlsx";


    /**
     *
     * @param sourcePath 绝对路径
     */
    public JsI18nProcessor(String sourcePath){
        this.sourcePath = sourcePath;
    }

    public JsI18nProcessor(String sourcePath, String i18nKeyPrefix, String i18nGlobal){
        this.sourcePath = sourcePath;
        this.i18nKeyPrefix = i18nKeyPrefix;
        this.i18nGlobal = i18nGlobal;
    }

    public static void main(String[] args) {
//        List<JsParseVO> jsParseVOS = JsParser.scanAllJsComment(new File("E:\\gitee\\i18n-convert\\demo\\test.js"));
//            List<JsParseVO> jsParseVOList = JsParser.scanAllJsComment(new File("E:\\gitee\\i18n-convert\\demo\\test.js"));
//        JsI18nProcessor jsI18nProcessor = new JsI18nProcessor("E:\\gitee\\i18n-convert\\demo\\test.js");
        JsI18nProcessor jsI18nProcessor = new JsI18nProcessor("E:\\gitee\\i18n-convert\\demo");
        jsI18nProcessor.getChineseWord();
//        List<I18nBean> allI18nMsgList = jsI18nProcessor.getAllI18nMsgList();
//        System.out.println(JSON.toJSONString(allI18nMsgList, true));

    }

    /**
     * //            String content = I18nUtils.file2String(file, ENCODING);
     * //            content = p_html.matcher(content).replaceAll("");
     * //            content = p_js1.matcher(content).replaceAll("");
     * //            content = p_js2.matcher(content).replaceAll("\n");
     */
    public void getChineseWord() {
        List<File> jsFiles = getJsFiles(this.sourcePath);
        int fileSize = 0;
        int needToI18nSize = 0;
        boolean needToI18n = false;
        for (File file : jsFiles) {
            fileSize++;
            needToI18n = false;

            List<JsParseVO> jsParseVOList = JsParser.scanAllJsComment(file);
//            System.out.println(JSON.toJSONString(jsParseVOList, true));
            // 原始内容
            List<String> srcContentList = new ArrayList<>();
            // 替换后内容
            List<String> replaceList = new ArrayList<>();
            // 匹配所有中文内容
            for (int i = 0; i < jsParseVOList.size(); i++) {
                JsParseVO vo = jsParseVOList.get(i);
                String content = vo.getContent();
                srcContentList.add(content);
                if (vo.isCommentFlag()) {
                    // 注释内容
                    replaceList.add(content);
                } else {
                    String needReplaceContent = content;
                    // 包含中文才处理
                    if (PatternUtils.containsChinese(needReplaceContent)) {
//                        System.out.println("===========" + needReplaceContent);
                        needToI18n = true;
                        needReplaceContent = getI18nString(file, needReplaceContent, PatternUtils.SINGLE_QUOT);
                        needReplaceContent = getI18nString(file, needReplaceContent, PatternUtils.DOUBLE_QUOT);
                    }
                    // 添加处理后内容
                    replaceList.add(needReplaceContent);
                }
            }
            if (needToI18n) {
                needToI18nSize++;
            }
            createReplaceJs(file, replaceList);
        }
        // 写出翻译内容到excel,（国际化key	| 原始内容	| 需翻译内容	| 英文内容	| 包含此内容的文件）
        writeExcel(allI18nMsgList);
        // 获取文件中的翻译词,  key 文件名，val 为 i18nBean list
        Map<String, List<I18nBean>> fileI18nMap = getFileI18nMap();
        //        translate();
        createIL8NFile(fileI18nMap);
//        allI18nMsgList
        System.out.println("************");
        System.out.println("扫描js文件数量: " + fileSize);
        System.out.println("国际化转化js文件数量:" + needToI18nSize);
        System.out.println("************");

    }

    private void createReplaceJs(File file, List<String> replaceList) {

        StringBuilder contentSb = new StringBuilder(128);
        if (CollectionUtils.isNotEmpty(replaceList)) {
            for (int i = 0; i < replaceList.size(); i++) {
                contentSb.append(replaceList.get(i));
            }
        }
        File sourceFile = new File(this.sourcePath);
        if (sourceFile.isDirectory()) {
            // 生成相对目录
            String fileOutPath = StringUtils.replace(file.getAbsolutePath(), this.sourcePath, getBaseOutDir());
            // 获取最后一级目录
            int index = StringUtils.lastIndexOf(fileOutPath, File.separator);
            File substringFile = new File(fileOutPath.substring(0, index));
            if (!substringFile.exists()) {
                substringFile.mkdirs();
            }
            // 写文件到
            try (Writer writer = new FileWriter(fileOutPath)) {
                IOUtils.write(contentSb.toString(), writer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            String fileName = sourceFile.getName();
            try (Writer writer = new FileWriter(getBaseOutDir() + fileName)) {
                IOUtils.write(contentSb.toString(), writer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void createIL8NFile(Map<String, List<I18nBean>> fileI18nMap) {
        // 加载翻译内容 key中文 , value bean 为各个对象
        Map<String, I18nTranslationData> stringI18nTranslationDataMap = loadI18nTranslation(new File(i18nTranslationFileName));
        // 全局 global
        if (CollectionUtils.isNotEmpty(allI18nMsgList)) {
            JSONObject objZh = new JSONObject();
            JSONObject objEn = new JSONObject();
            for (int i = 0; i < allI18nMsgList.size(); i++) {
                I18nBean i18nBean = allI18nMsgList.get(i);
                // i18nBean.getKey() 删除 I18nMsg. 前缀
                objZh.put(this.getI18nObjKey(i18nBean.getKey()), i18nBean.getChinese());

                String english = getEnglishString(stringI18nTranslationDataMap, i18nBean);
                objEn.put(this.getI18nObjKey(i18nBean.getKey()), english);
            }
            writeFile(objZh, getBaseOutDir() + "global_zh-cn.js");
            writeFile(objEn, getBaseOutDir() + "global_en-us.js");
        }
        if (fileI18nMap != null) {
            Set<String> filePathSet = fileI18nMap.keySet();
            for (String filelPath : filePathSet) {
                List<I18nBean> i18nBeanList = fileI18nMap.get(filelPath);
                // 国际化文件对象
                JSONObject objZh = new JSONObject();
                // 英文
                JSONObject objEn = new JSONObject();
                for (int i = 0; i < i18nBeanList.size(); i++) {
                    I18nBean i18nBean = i18nBeanList.get(i);
                    objZh.put(this.getI18nObjKey(i18nBean.getKey()), i18nBean.getChinese());
                    String english = getEnglishString(stringI18nTranslationDataMap, i18nBean);
                    objEn.put(this.getI18nObjKey(i18nBean.getKey()), english);
                }
                System.out.println("需做国际化原js文件路径："  +  filelPath);

                File sourceFile = new File(this.sourcePath);
                if (sourceFile.isDirectory()) {
                    // 生成相对目录
                    String fileOutPath = StringUtils.replace(filelPath, this.sourcePath, getBaseOutDir());
                    String substringFileName = fileOutPath.substring(0, fileOutPath.length() - 3);
                    // 获取最后一级目录
                    int index = StringUtils.lastIndexOf(substringFileName, File.separator);
                    File substringFile = new File(substringFileName.substring(0, index));
                    if (!substringFile.exists()) {
                        substringFile.mkdirs();
                    }

                    // 写文件到
                    writeFile(objZh, substringFileName + "_zh-cn.js");
                    writeFile(objEn, substringFileName + "_en-us.js");
                } else {
                    String fileName = sourceFile.getName();
                    String substringFileName = fileName.substring(0, fileName.length() - 3);
                    writeFile(objZh, getBaseOutDir() + substringFileName + "_zh-cn.js");
                    writeFile(objEn, getBaseOutDir() + substringFileName + "_en-us.js");
                }
            }

        }
    }

    /**
     * 获取英文翻译
     * <p>
     *     如果没有英文翻译信息，默认使用中文
     * </p>
     * @param stringI18nTranslationDataMap
     * @param i18nBean
     * @return
     */
    private String getEnglishString(Map<String, I18nTranslationData> stringI18nTranslationDataMap, I18nBean i18nBean) {
        String english = i18nBean.getChinese();
        I18nTranslationData i18nTranslationData = stringI18nTranslationDataMap.get(i18nBean.getChinese());
        if (i18nTranslationData != null && StringUtils.isNotEmpty(i18nTranslationData.getEnglish())) {
            english = i18nTranslationData.getEnglish();
        } else {
            logger.warn(i18nBean.getChinese() + " === 没有英文翻译");
        }
        return english;
    }

    private Map<String, I18nTranslationData> loadI18nTranslation(File file) {
        Map<String, I18nTranslationData>  map = new HashMap<>();
        if (file.exists()) {
            EasyExcel.read(file, I18nTranslationData.class, new I18nTranslationDataListener(map)).sheet().doRead();
        } else {
            // 国际化翻译文件不存在
            logger.warn("翻译文件 i18n_translation.xlsx 不存在");
        }
        logger.debug(JSON.toJSONString(map));
        return map;
    }

    private String getI18nObjKey(String i18nBeanKey) {
        return i18nBeanKey.split("\\.")[1];
    }

    /**
     * 按文件写 i18n 对象
     * @param obj
     * @param filePath
     */
    private void writeFile(JSONObject obj, String filePath) {
        String content = "var " +  i18nGlobal + " = ";
        content += JSON.toJSONString(obj, true);
        try (Writer writer = new FileWriter(filePath)) {
            IOUtils.write(content, writer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private Map<String, List<I18nBean>> getFileI18nMap() {
        Map<String, List<I18nBean>> fileI18nMap = new HashMap<>();
        for (int i = 0; i < allI18nMsgList.size(); i++) {
            I18nBean i18nBean = allI18nMsgList.get(i);
            String[] filePatshArr = StringUtils.split(i18nBean.getFilePaths(), ";");
            for (int j = 0; j < filePatshArr.length; j++) {
                String filePath = filePatshArr[j];
                if (fileI18nMap.containsKey(filePath)) {
                    fileI18nMap.get(filePath).add(i18nBean);
                } else {
                    List<I18nBean> list = new ArrayList<>();
                    list.add(i18nBean);
                    fileI18nMap.put(filePath, list);
                }
            }
        }
        return fileI18nMap;
    }

    /**
     * 获取输出内容的基础目录
     * @return
     */
    private String getBaseOutDir() {
        String dirPath = System.getProperty("user.dir") + "/dest/";
        File saveDir = new File(dirPath);
        if (!saveDir.exists()) {
            saveDir.mkdirs();
        }
        return dirPath;
    }

    private void writeExcel(List<I18nBean> allI18nMsgList) {
        // TestFileUtil.getPath()
        String baseDirPath = getBaseOutDir();
//        String fileName = System.getProperty("user.dir") + "/dest/i18n_global_" + System.currentTimeMillis() + ".xlsx";
        String fileName = baseDirPath + "i18n_global.xlsx";
        // 这里 需要指定写用哪个class去读，然后写到第一个sheet，名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
//        EasyExcel.write(fileName, I18nBean.class).sheet("全局翻译内容").doWrite(data());
        List<I18nExcel> dataList = new ArrayList<>();
        for (int i = 0; i < allI18nMsgList.size(); i++) {
            I18nBean i18nBean = allI18nMsgList.get(i);
            I18nExcel i18nExcel = new I18nExcel();
            i18nExcel.setFilePaths(i18nBean.getFilePaths());
            i18nExcel.setKey(i18nBean.getKey());
            i18nExcel.setContent(i18nBean.getContent());
            i18nExcel.setChinese(i18nBean.getChinese());
            i18nExcel.setEnglish(i18nBean.getEnglish());
            dataList.add(i18nExcel);
        }
        EasyExcel.write(fileName, I18nExcel.class).sheet("全局翻译内容").doWrite(dataList);
    }

    /**
     * 替换中文为 i18n key
     * @param file
     * @param needReplaceContent
     * @param pattern
     * @return
     */
    private String getI18nString(File file, String needReplaceContent, Pattern pattern) {
        // 原字符串
        String srcContent = needReplaceContent;
        Matcher matcher = pattern.matcher(needReplaceContent);
        while (matcher.find()) {
            String group = matcher.group();
            System.out.printf("匹配中文内容group: %s\n", matcher.group());
            if (PatternUtils.containsChinese(group)) {
                // 包含中文才处理
                // 找出双引号和单引号中的内容 特殊 \" 和 \' 非结尾
                if (group.contains("\\\"") || group.contains("\\'")) {
                    // 包含特殊 \" 字符
                    // 保存未解析的错误信息
                    if (problemFileMap.containsKey(file.getAbsolutePath())) {
                        problemFileMap.put(file.getAbsolutePath(), problemFileMap.get(file.getAbsolutePath()) + group);
                    } else {
                        problemFileMap.put(file.getAbsolutePath(), group);
                    }

                    System.out.println("含有特殊字符未被替换内容的问题文件路径: " + file.getAbsolutePath());
                    System.out.println("问题内容: " + group);
                    /*System.out.println(needReplaceContent);
                    // 有特殊字符的文件不做内容处理
                    needReplaceContent = srcContent;
                    break;*/
                }

                // 删除第一个和最后一个字符，这里删除单引号或双引号,去除空格
                String delSingleAndDoubleQote = group.substring(1, group.length() - 1).trim();
                String i18nKey = i18nGlobal + "." + ChineseToPinyin.getPingYin(delSingleAndDoubleQote);
                // 去除前后两个字符内容
                String chineseContentTrim = delSingleAndDoubleQote.trim();
                if (i18nMap.containsKey(chineseContentTrim)) {
                    i18nMap.get(chineseContentTrim).setFile(file.getAbsolutePath());
                } else {
                    // 获取信息
                    I18nBean i18nBean = new I18nBean();
                    // 国际化key
                    i18nBean.setKey(i18nKey);
                    i18nBean.setContent(group);
                    // 刪除前后 ' 和 " 字符
                    i18nBean.setChinese(chineseContentTrim);
//                    i18nBean.setEnglish(chineseContentTrim);
                    i18nBean.setFile(file.getAbsolutePath());
                    allI18nMsgList.add(i18nBean);
                    i18nMap.put(delSingleAndDoubleQote, i18nBean);
                }
                // 替换内容
                needReplaceContent = needReplaceContent.replaceAll(group, i18nKey);
            }
        }
        return needReplaceContent;
    }


    private static List<File> getJsFiles(String sourcePath) {
        List<File> files = I18nUtils.getFiles(sourcePath, ".js");
        // 排除原有国际化文件
        List<File> collectFiles = files.stream().filter(file -> !file.getName().toLowerCase().endsWith("_zh-cn.js") && !file.getName().toLowerCase().endsWith("_en-us.js")).collect(Collectors.toList());
        System.out.println("************");
        System.out.println("需要处理的所有js文件:");
        System.out.println(JSON.toJSONString(collectFiles, true));
        System.out.println("************");
        return collectFiles;
    }
}
